home *** CD-ROM | disk | FTP | other *** search
- //////////
- //
- // File: QTFileTransfer.c
- //
- // Contains: Sample code for transferring a file asynchronously from a web server.
- //
- // Written by: Tim Monroe
- //
- // Copyright: © 1998-1999 by Apple Computer, Inc., all rights reserved.
- //
- // Change History (most recent first):
- //
- // <5> 03/03/99 rtm switched to using DataHReadAsync; removed unnecessary NOTES
- // <4> 12/25/99 rtm added NOTES concerning DataHReadAsync and DataHGetFileSize
- // <3> 11/30/98 rtm modified code to use GetDataHandler instead of FindNextComponent;
- // moved call to QTFileTrans_CloseDownHandlers out of completion proc
- // <2> 11/16/98 rtm got asynchronous ftp and http file transfer working
- // <1> 11/11/98 rtm first file
- //
- // QuickTime Streaming has ftp and http data handlers, which you can use to transfer files
- // synchronously or asynchronously from a web server. This sample code illustrates how to
- // perform asynchronous transfers. In all likelihood, you'll want to transfer asynchronously,
- // since your application can continue to operate while the transfer is underway.
- //
- // The basic idea is to instantiate the URL data handler and the HFS data handler; the URL
- // data handler will be reading data from a remote ftp or http file into a buffer, and the
- // HFS data handler will be writing data from that buffer into a local file. This reading and
- // writing continues until the file is completely transferred.
- //
- // To transfer a remote file to the local machine, call the QTFileTrans_CopyRemoteFileToLocalFile
- // function defined here. It does all the necessary set-up and schedules the first read request;
- // all subsequent write and read requests are scheduled by the read and write completion routines.
- // Note that, when doing asynchronous transfers, you need to give time to the data handlers by
- // calling DataHTask periodically; on the Mac, you can put code like this in your main event loop:
- //
- // // if we're done, close down the data handlers
- // if (gDoneTransferring)
- // QTFileTrans_CloseDownHandlers();
- //
- // // give the data handlers some time, if they are still active
- // if (gDataReader != NULL)
- // DataHTask(gDataReader);
- //
- // if (gDataWriter != NULL)
- // DataHTask(gDataWriter);
- //
- // On Windows, you could install a timer that calls this code at a specified interval. (On either
- // platform, you should probably also implement some way of making sure that the user doesn't quit
- // the application while a transfer is underway.)
- //
- // NOTES:
- //
- // *** (1) ***
- // For information about the main routines used here, see the chapter "Data Handler Components" in
- // the document QT3.0Reference.pdf.
- //
- // *** (2) ***
- // The code for implementing synchronous transfers is actually much simpler: you don't need any
- // completion routines, and the "scheduling" is much easier. Here's an outline of all you need to do
- // to transfer a file synchronously:
- //
- // DataHGetData(gDataReader, myDataBuffer, 0L, 0L, kDataBufferSize);
- // DataHCloseForRead(gDataReader);
- // DataHPutData(gDataWriter, myDataBuffer, 0L, NULL, kDataBufferSize);
- // DataHCloseForWrite(gDataWriter);
- //
- // In this case, however, myDataBuffer is a handle, not a pointer. (Also, we've assumed that the file
- // being transferred fits completely into the buffer; you could easily fix that assumption.)
- //
- // *** (3) ***
- // You'll notice that we use our completion routines to schedule subsequent data reads and writes.
- // This is okay because data handler completion routines are never called at interrupt time.
- //
- // *** (4) ***
- // In some instances, DataHGetFileSize is not able to determine the size of the file to be downloaded
- // (for example, an FTP server might not support the SIZE command). A more general strategy therefore
- // would be to download a file until you get eofErr. Implementing this strategy is left as an exercise
- // for the reader.
- //
- //////////
-
-
- #include "QTFileTransfer.h"
-
- //global variables
- ComponentInstance gDataReader = NULL; // the data handler that reads data from the URL
- ComponentInstance gDataWriter = NULL; // the data handler that writes data to an HFS file
- DataHCompletionUPP gReadDataHCompletionUPP = NULL;
- DataHCompletionUPP gWriteDataHCompletionUPP = NULL;
- long gBytesToTransfer = 0L; // the number of bytes to transfer
- long gBytesTransferred = 0L; // the number of bytes already transferred
- Boolean gDoneTransferring = false; // are we done transferring data?
-
-
- //////////
- //
- // QTFileTrans_CopyRemoteFileToLocalFile
- // Copy a remote file (located at the specified URL) into a local file.
- //
- //////////
-
- OSErr QTFileTrans_CopyRemoteFileToLocalFile (char *theURL, FSSpecPtr theFSSpecPtr)
- {
- Handle myReaderRef = NULL; // data reference for the remote file
- Handle myWriterRef = NULL; // data reference for the local file
- Ptr myDataBuffer = NULL; // buffer that holds data being transferred
- Size mySize = 0;
- ComponentResult myErr = badComponentType;
-
- //////////
- //
- // create a data reference for the remote file
- //
- //////////
-
- // get the size of the URL, plus the terminating null byte
- mySize = (Size)strlen(theURL) + 1;
- if (mySize == 0)
- goto bail;
-
- // allocate a new handle
- myReaderRef = NewHandleClear(mySize);
- if (myReaderRef == NULL)
- goto bail;
-
- // copy the URL into the handle
- BlockMove(theURL, *myReaderRef, mySize);
-
- //////////
- //
- // create a data reference for the local file
- //
- //////////
-
- // delete the target local file, if it already exists;
- // if it doesn't exist yet, we'll get an error (fnfErr), which we just ignore
- FSpDelete(theFSSpecPtr);
-
- myWriterRef = NewHandleClear(sizeof(Handle));
- if (myWriterRef == NULL)
- goto bail;
-
- // create the local file
- myErr = FSpCreate(theFSSpecPtr, kTransFileCreator, kTransFileType, smSystemScript);
- if (myErr != noErr)
- goto bail;
-
- myErr = QTNewAlias(theFSSpecPtr, (AliasHandle *)&myWriterRef, true);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // find and open the Apple URL and HFS data handlers; connect the data references to them
- //
- //////////
-
- gDataReader = OpenComponent(GetDataHandler(myReaderRef, URLDataHandlerSubType, kDataHCanRead));
- if (gDataReader == NULL)
- goto bail;
-
- gDataWriter = OpenComponent(GetDataHandler(myWriterRef, rAliasType, kDataHCanWrite));
- if (gDataWriter == NULL)
- goto bail;
-
- // set the data reference for the URL data handler
- myErr = DataHSetDataRef(gDataReader, myReaderRef);
- if (myErr != noErr)
- goto bail;
-
- // set the data reference for the HFS data handler
- myErr = DataHSetDataRef(gDataWriter, myWriterRef);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // allocate a data buffer; the URL data handler copies data into this buffer,
- // and the HFS data handler copies data out of it
- //
- //////////
-
- myDataBuffer = NewPtrClear(kDataBufferSize);
- myErr = MemError();
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // connect to the remote and local files
- //
- //////////
-
- // open a read-only path to the remote data reference
- myErr = DataHOpenForRead(gDataReader);
- if (myErr != noErr)
- goto bail;
-
- // get the size of the remote file
- myErr = DataHGetFileSize(gDataReader, &gBytesToTransfer);
- if (myErr != noErr)
- goto bail;
-
- // open a write-only path to the local data reference
- myErr = DataHOpenForWrite(gDataWriter);
- if (myErr != noErr)
- goto bail;
-
- //////////
- //
- // start reading and writing data
- //
- //////////
-
- gDoneTransferring = false;
- gBytesTransferred = 0L;
-
- gReadDataHCompletionUPP = NewDataHCompletionProc(QTFileTrans_ReadDataCompletionProc);
- gWriteDataHCompletionUPP = NewDataHCompletionProc(QTFileTrans_WriteDataCompletionProc);
-
- // start retrieving the data; we do this by calling our own write completion routine,
- // pretending that we've just successfully finished writing 0 bytes of data
- QTFileTrans_WriteDataCompletionProc(myDataBuffer, 0L, noErr);
-
- bail:
- // if we encountered any error, close the data handler components
- if (myErr != noErr)
- QTFileTrans_CloseDownHandlers();
-
- return((OSErr)myErr);
- }
-
-
- //////////
- //
- // QTFileTrans_ReadDataCompletionProc
- // This procedure is called when the data handler has completed a read operation.
- //
- // The theRefCon parameter contains the number of bytes just read.
- //
- //////////
-
- void QTFileTrans_ReadDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr)
- {
- #pragma unused(theErr)
-
- // we just finished reading some data, so schedule a write operation
- DataHWrite( gDataWriter,
- theRequest, // the data buffer
- gBytesTransferred, // write from the current offset
- theRefCon, // the number of bytes to write
- gWriteDataHCompletionUPP,
- theRefCon);
- }
-
-
- //////////
- //
- // QTFileTrans_WriteDataCompletionProc
- // This procedure is called when the data handler has completed a write operation.
- //
- // The theRefCon parameter contains the number of bytes just written.
- //
- //////////
-
- void QTFileTrans_WriteDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr)
- {
- #pragma unused(theErr)
-
- long myNumBytesToRead;
- wide myWide;
-
- // increment our tally of the number of bytes written so far
- gBytesTransferred += theRefCon;
-
- if (gBytesTransferred < gBytesToTransfer) {
- // there is still data to read and write, so schedule a read operation
-
- // determine how big a chunk to read
- if (gBytesToTransfer - gBytesTransferred > kDataBufferSize)
- myNumBytesToRead = kDataBufferSize;
- else
- myNumBytesToRead = gBytesToTransfer - gBytesTransferred;
-
- myWide.lo = gBytesTransferred; // read from the current offset
- myWide.hi = 0;
-
- // schedule a read operation
- DataHReadAsync(gDataReader,
- theRequest, // the data buffer
- myNumBytesToRead,
- &myWide,
- gReadDataHCompletionUPP,
- myNumBytesToRead);
-
- } else {
- // we've transferred all the data, so set a flag to tell us to close down the data handlers
- gDoneTransferring = true;
- }
-
- }
-
-
- //////////
- //
- // QTFileTrans_CloseDownHandlers
- // Close our read/write access to our data references and then close down the read/write data handlers.
- //
- //////////
-
- void QTFileTrans_CloseDownHandlers (void)
- {
- if (gDataReader != NULL) {
- DataHCloseForRead(gDataReader);
- CloseComponent(gDataReader);
- gDataReader = NULL;
- }
-
- if (gDataWriter != NULL) {
- DataHCloseForWrite(gDataWriter);
- CloseComponent(gDataWriter);
- gDataWriter = NULL;
- }
- }